﻿using ICSharpCode.SharpZipLib.Zip;
using MiniFox.Components;
using MiniFox.Platform.File.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MiniFox.Platform.File
{
    /// <summary>
    /// 
    /// </summary>
    [AutoRegister, SpareComponent(typeof(FileDeCompressor))]
    public class FileDeCompressor : IDisposable, IAsyncDisposable
    {
        Stream _stream;
        ZipInputStream _zipStream;
        #region 构造
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        public FileDeCompressor(string fileName) : this(new FileStream(fileName, FileMode.Open, FileAccess.Read))
        {

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        public FileDeCompressor(byte[] data) : this(new MemoryStream(data))
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="baseStream"></param>
        protected FileDeCompressor(Stream baseStream)
        {
            this._stream = baseStream;
            Init();
        }
        #endregion

        private Stream BaseStream
        {
            get { return _stream; }
        }

        /// <summary>
        /// 
        /// </summary>
        protected void Init()
        {
            this._zipStream = new ZipInputStream(BaseStream);

        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public ZipFileData ExtraFile()
        {
            ZipFileData zipFileData = new ZipFileData();
            do
            {
                ZipEntry zipEntry = this._zipStream.GetNextEntry();
                if (zipEntry == null)
                    break;
                PackFile packFile = new PackFile();
                if (zipEntry.IsDirectory)
                {
                    packFile.Path = zipEntry.Name;
                }
                else
                {
                    string[] segments = zipEntry.Name.Split('/');
                    packFile.FileName = segments.Last();
                    if (segments.Length > 1)
                    {
                        packFile.Path = zipEntry.Name.Replace(segments.Last(), string.Empty);
                    }

                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        int cnt = 0;
                        while (true)
                        {
                            byte[] data = new byte[1024];
                            cnt = this._zipStream.Read(data, 0, data.Length);
                            if (cnt > 0)
                            {
                                memoryStream.Write(data, 0, cnt);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }

                }

                zipFileData.PackedFiles.Add(packFile);
            }
            while (true);
            return zipFileData;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public async Task<ZipFileData> ExtraFileAsync()
        {
            ZipFileData zipFileData = new ZipFileData();
            do
            {
                ZipEntry zipEntry = this._zipStream.GetNextEntry();
                if (zipEntry == null)
                    break;
                PackFile packFile = new PackFile();
                if (zipEntry.IsDirectory)
                {
                    packFile.Path = zipEntry.Name;
                }
                else
                {
                    string[] segments = zipEntry.Name.Split('/');
                    packFile.FileName = segments.Last();
                    if (segments.Length > 1)
                    {
                        packFile.Path = zipEntry.Name.Replace(segments.Last(), string.Empty);
                    }

                    await using (MemoryStream memoryStream = new MemoryStream())
                    {
                        int cnt = 0;
                        while (true)
                        {
                            byte[] data = new byte[1024];
                            cnt = await this._zipStream.ReadAsync(data, 0, data.Length);
                            if (cnt > 0)
                            {
                                await memoryStream.WriteAsync(data, 0, cnt);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }

                }

                zipFileData.PackedFiles.Add(packFile);
            }
            while (true);

            return zipFileData;
        }
        /// <summary>
        /// 
        /// </summary>
        private void Disposing()
        {
            if (this.BaseStream != null)
            {
                this.BaseStream.Close();
                this.BaseStream.Dispose();
            }
            if (this._zipStream != null)
            {
                this._zipStream.Close();
                this._zipStream.Dispose();
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            Disposing();
        }
        /// <summary>
        /// 
        /// </summary>
        private async ValueTask DisposingAsync()
        {
            if (this.BaseStream != null)
            {
                this.BaseStream.Close();
                await this.BaseStream.DisposeAsync();
            }
            if (this._zipStream != null)
            {
                this._zipStream.Close();
                await this._zipStream.DisposeAsync();
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public async ValueTask DisposeAsync()
        {
            await DisposingAsync();
        }
    }
}
